package csu.softwaremetric.service;

import com.alibaba.fastjson.JSONObject;
import org.springframework.stereotype.Service;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.FileInputStream;
import java.io.InputStream;

@Service
public class CyclomaticComplexityMetricService {

    public JSONObject getCyclomaticComplexity(String path)
    {
        JSONObject jsonObject = new JSONObject();
        int result = doParse(path);
        if(result >= 0)
        {
            jsonObject.put("STATUS", "SUCCESS");
            jsonObject.put("MSG", "成功");
            jsonObject.put("CyclomaticComplexity", result);
        }
        else if(result == -1)
        {
            jsonObject.put("STATUS", "FAILURE");
            jsonObject.put("MSG", "控制流图有误");
        }
        else
        {
            jsonObject.put("STATUS", "FAILURE");
            jsonObject.put("MSG", "文件不存在");
        }
        return jsonObject;
    }

    private int doParse(String path) {
        //1-获取XML-IO流
        InputStream xmlInputStream = getXmlInputStream(path);
        //2-解析XML-IO流 ，获取Document 对象，以及Document对象 的根节点
        Element rootElement;
        try {
            rootElement = getRootElementFromIs(xmlInputStream);
            //3~5-从根元素解析得到元素
            return parseElementFromRoot(rootElement);
        } catch (Exception e) {
            return -2;
        }
    }

    //1-获取XML-IO流
    private InputStream getXmlInputStream(String xmlPath) {
        InputStream inputStream;
        try {
            //1-把要解析的 XML 文档转化为输入流，以便 DOM 解析器解析它
            inputStream = new FileInputStream(xmlPath);
        } catch (Exception e) {
            return null;
        }
        return inputStream;
    }

    //2-解析XML-IO流 ，获取Document 对象，以及Document对象 的根节点
    private Element getRootElementFromIs(InputStream inputStream) throws Exception {
        if (inputStream == null) {
            return null;
        }
        /*
         * javax.xml.parsers 包中的DocumentBuilderFactory用于创建DOM模式的解析器对象 ，
         * DocumentBuilderFactory是一个抽象工厂类，它不能直接实例化，但该类提供了一个newInstance方法 ，
         * 这个方法会根据本地平台默认安装的解析器，自动创建一个工厂的对象并返回。
         */
        //2-调用 DocumentBuilderFactory.newInstance() 方法得到创建 DOM 解析器的工厂
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        //3-调用工厂对象的 newDocumentBuilder方法得到 DOM 解析器对象。
        DocumentBuilder docBuilder = factory.newDocumentBuilder();
        //4-调用 DOM 解析器对象的 parse() 方法解析 XML 文档，得到代表整个文档的 Document 对象，进行可以利用DOM特性对整个XML文档进行操作了。
        Document doc = docBuilder.parse(inputStream);
        //5-得到 XML 文档的根节点
        Element root = doc.getDocumentElement();
        //6-关闭流
        inputStream.close();
        return root;
    }

    //3-从根元素解析得到元素
    private int parseElementFromRoot(Element root) {
        NodeList nl = root.getChildNodes();
        for (int i = 0; i < nl.getLength(); i++) {
            Node node = nl.item(i);
            if (node instanceof Element) {
                Element ele = (Element) node;
                return  calculateCyclomaticComplexity(ele);
            }
        }
        return -1;
    }

    private int calculateCyclomaticComplexity(Element ele) {
        int nodeNum = 0;
        int edgeNum = 0;
        int decisionNum = 0;
        if (ele.getAttribute("xmi:type").equals("uml:Activity")) {
            NodeList propertyEleList = ele.getElementsByTagName("node");//根据标签名称获取标签元素列表
            for (int i = 0; i < propertyEleList.getLength(); i++) {
                Node node = propertyEleList.item(i);
                if (node instanceof Element) {
                    Element propertyEle = (Element) node;
                    String type = propertyEle.getAttribute("xmi:type");
                    if(type.equals("uml:OpaqueAction") || type.equals("uml:InitialNode") || type.equals("uml:FlowFinalNode"))
                    {
                        nodeNum++;
                    }
                    else if (type.equals("uml:DecisionNode")) {
                        decisionNum++;
                    }
                }
            }
            propertyEleList = ele.getElementsByTagName("edge");//根据标签名称获取标签元素列表
            for (int i = 0; i < propertyEleList.getLength(); i++) {
                Node node = propertyEleList.item(i);
                if (node instanceof Element) {
                    Element propertyEle = (Element) node;
                    String type = propertyEle.getAttribute("xmi:type");
                    if(type.equals("uml:ControlFlow"))
                    {
                        edgeNum++;
                    }
                }
            }
        }
        if(decisionNum + 1 == edgeNum - nodeNum - decisionNum + 2) {
            return decisionNum + 1;
        }
        else {
            return -1;
        }
    }
}
